#include <queue>
#include <pthread.h>
#include <iostream>

const int defaultcap = 5;

template <class T>
class BlockQueue
{
private:
    bool Isfull()
    {
        return _q.size() == _cap;
    }
    bool Isempty()
    {
        return _q.empty();
    }

public:
    BlockQueue(int cap = defaultcap)
        : _cap(defaultcap), _psleep_num(0), _csleep_num(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_full_cond, nullptr);
        pthread_cond_init(&_empty_cond, nullptr);
    }
    void Equeue(const T &t)
    {
        pthread_mutex_lock(&_mutex);
        while(Isfull())
        {
            _psleep_num++;
            std::cout << "生产者进入休眠" << std::endl;
            pthread_cond_wait(&_full_cond,&_mutex);
            _psleep_num--;
        }

        if(_csleep_num > 0)//如果没有休眠消费者就不需要唤醒
        {
            std::cout << "唤醒消费者..." << std::endl;
            pthread_cond_signal(&_empty_cond);
            _csleep_num--;
        }
        _q.push(t);//先调用拷贝构造形成T类型变量，再插入
        pthread_mutex_unlock(&_mutex);
    }
    T Pop()
    {
        pthread_mutex_lock(&_mutex);
        while(Isempty())
        {
            _csleep_num++;
            std::cout << "消费者进入休眠" << std::endl;
            pthread_cond_wait(&_empty_cond,&_mutex);
            _csleep_num--;
        }
        if(_psleep_num)
        {
            std::cout << "唤醒生产者..." << std::endl;
            pthread_cond_signal(&_full_cond);
            _psleep_num--;
        }
        T t = _q.front();
        _q.pop();//pop的返回值是void，这样设计是为了保证构造t的时候不会因为抛出异常而导致值丢失，如果抛出异常，那此时还没有pop

        pthread_mutex_unlock(&_mutex);
        return t;
    }
    ~BlockQueue()
    {
    }

private:
    std::queue<T> _q; // 临界资源
    int _cap;

    pthread_mutex_t _mutex;
    pthread_cond_t _full_cond;
    pthread_cond_t _empty_cond;

    int _psleep_num;
    int _csleep_num;
};